#include "plateaurisq.h"

#include "frontieregraphics.h"
#include "etatgraphics.h"

#include "etat.h"
#include "frontiere.h"
#include "joueur.h"
#include "pion.h"
#include "missions.h"

#include "log.h"

#include <QtXml>
#include <QtAlgorithms>

PlateauRisQ::PlateauRisQ() :
    ressourcesPath(":/boards/")
{
    this->status = RIEN_FAIRE;
    moi = NULL;
    // ces booléens sont sans doute inutil vu qu'on passe le numéro d'étape
    // à faire à tout le monde mais on peut toujours se servir de ces booléens
    // quand on veut savoir où on en est et qu'on n'a pas accès au numéro d'étape
    // passé en paramètre habituellement.
    placement_Initial = true;
    joueur_changed = false ;
    placement_renforts = false ;
    attaques = false ;
    deplacement_troupes = false ;
    // on commence par l'id n°0
    Joueur_en_cours = 0 ;
    Joueurs_ayant_restant_nul = 0 ;

    etat_attaquant = -1 ;
    etat_cible = -1 ;

}


bool PlateauRisQ::chargerPlateau(QString boardname){
    int r;
    if ( (r = chargerXml(ressourcesPath + boardname + ".xml")) < 0 ){
        LOG(OERREUR, "problème lors de l'ouverture du xml du plateau");
        return false;
    }
    this->nomPlateau = boardname;


    return true;
}

int PlateauRisQ::chargerXml(QString filename)
{

    QDomDocument doc( "Map" );
    QFile file( filename );
    if( !file.open(QFile::ReadOnly) )
        return -1;

    if( !doc.setContent( &file ) ){
        file.close();
        return -2;
    }

    file.close();

    // On verifie que la racine et OK
    QDomElement root = doc.documentElement();
    if( root.tagName() != "board" )
        return -3;

    QDomNode n = root.firstChild();
    while( !n.isNull() ) {
        QDomElement continentElem = n.toElement();
        if( !continentElem.isNull() && continentElem.tagName() != "continent") {
            continue;
        }

        // Ajoute un continent
        Continent *c = new Continent;
        c->id = continentElem.attribute("id", "0").toInt();
        c->nom = continentElem.attribute("name", "unset");
        continentsList.append(c);

        QDomNode no = continentElem.firstChild();
        // On parcours les etats du continent et on instancie les noeuds
        while( !no.isNull() ) {
          QDomElement e = no.toElement();
          if( !e.isNull() && e.tagName() == "pays") {
                int id = e.attribute("id", "0").toInt();

                Etat *etat = new Etat(id, e.attribute("name", "unset"), c, this);
                etat->setPos(e.attribute("x", "0").toInt() , e.attribute("y", "0").toInt());

                connect(etat, SIGNAL(nouveauPion(Pion*)), this, SIGNAL(nouveauPion(Pion*)));
                connect(etat, SIGNAL(supprimerPion(Pion*)), this, SIGNAL(supprimerPion(Pion*)));

                emit nouveauEtat(etat);
                etatsList[id] = etat;
          }

          no = no.nextSibling();
        }

        // On fix les frontieres
        no = continentElem.firstChild();
        while( !no.isNull() ) {
            QDomElement paysElem = no.toElement();
            if( !paysElem.isNull() && paysElem.tagName() == "pays" ) {
                QDomNodeList frontList = paysElem.elementsByTagName("frontiere");
                for(int i = 0; i < frontList.count(); ++i){
                    QDomElement frontElem = frontList.at(i).toElement();
                    Frontiere *e = new Frontiere(etatsList[paysElem.attribute("id", "0").toInt()],
                                        etatsList[frontElem.text().toInt()]);

                    emit nouvelleFrontiere(e);

                }
            }

            no = no.nextSibling();
        }

        n = n.nextSibling();
    }

    return 0;
}

void PlateauRisQ::assigneEtat()
{
    QList<Etat*> etats = etatsList.values();

    int nbEtat = etats.count();
    int nbJoueur = joueursList.count();
    int nbEtatJoueur = nbEtat/nbJoueur;


    for (int i = 0; i < nbJoueur; ++i){
        for (int j = 0; j < nbEtatJoueur; ++j){
            int r = qrand() % etats.count();
            Etat *e = (Etat*)etats.at(r);
            e->setJoueur(joueursList.at(i));
            etats.removeAt(r);

            LOG2(ODEBUG,"Assigne %1 à %2",e->getNom(),
                joueursList.at(i)->getNom());
        }
    }


    if (etats.count() > 0){
        if (etats.count() > 1)
            LOG1(ODEBUG, "Il reste %1 etats", etats.count());
        else if (etats.count() == 1)
            LOG1(ODEBUG, "Il reste %1 etat", etats.count());

        foreach(Etat *e, etats){
            int r = qrand() % nbJoueur;
            e->setJoueur(joueursList.at(r));

            LOG2(ODEBUG,"Assigne %1 à %2",e->getNom(),
                joueursList.at(r)->getNom());
        }
    }

    for (int i = 0; i < nbJoueur; ++i){
        updateJoueurStatus(joueursList.at(i));
    }

    for (int i = 1; i < this->etats().count() ; i++)
        LOG2(ODEBUG,"id etat : %1 et joueur %2",this->etats()[i]->getId(),this->etats()[i]->getJoueur()->getId());



}

void PlateauRisQ::updateJoueurStatus(Joueur *j)
{
    int nbPion = 0;
    int nbPays = 0;

    /* Compte le nombre de pays et de pion que possède
     * un joueur et le met à jour */
    foreach(Etat *e, etatsList){
        if ( e->getJoueur() == j ){
            nbPays++;
            nbPion += e->pions().count();
        }
    }

    j->setNbEtat(nbPays);
    j->setNbPion(nbPion);
}

void PlateauRisQ::reset()
{
    foreach(Etat *e, etatsList){
        foreach(Pion *p, e->pions()){
            e->removePion(p);
        }
        e->setJoueur(NULL);
    }

    foreach(Joueur *j, joueursList){
        removeJoueur(j);
    }
}

void PlateauRisQ::creerObjectifs()
{
    QList<int> list_tmp;

    // Voir id des continents dans xml
    //  <continent id="1" name="North America">
    //  <continent id="2" name="South America">
    //  <continent id="3" name="Africa">

    // 18 territoires de 2 armées mini pendant 1 tour
    Missions* a = new Missions(18,2,1);
    objectifs.append(a);

    // 24 territoires pendant 1 tour
    Missions* b = new Missions(24,1);
    objectifs.append(b);

    // amérique du nord et afrique pendant 1 tour
    list_tmp.clear();
    list_tmp.append(1);
    list_tmp.append(3);
    Missions* c = new Missions(2,list_tmp,1);
    objectifs.append(c);

    // 3 continents dont europe et amerique sud pendant 1 tour
    list_tmp.clear();
    list_tmp.append(4);
    list_tmp.append(2);
    Missions* d = new Missions(3,list_tmp,1);
    objectifs.append(d);

    // 3 dont europe et océanie pendant 1 tour
    list_tmp.clear();
    list_tmp.append(4);
    list_tmp.append(6);
    Missions* e = new Missions(3,list_tmp,1);
    objectifs.append(e);

    // 2 continents, amérique nord et océanie pdt 1 tour
    list_tmp.clear();
    list_tmp.append(1);
    list_tmp.append(6);
    Missions* f = new Missions(2,list_tmp,1);
    objectifs.append(f);

    // asie et afrique
    list_tmp.clear();
    list_tmp.append(5);
    list_tmp.append(3);
    Missions* g = new Missions(2,list_tmp,1);
    objectifs.append(g);

    // asie et amérique sud
    list_tmp.clear();
    list_tmp.append(5);
    list_tmp.append(2);
    Missions* h = new Missions(2,list_tmp,1);
    objectifs.append(h);

    // détruire joueur 0
    Missions* i = new Missions(0);
    objectifs.append(i);

    // détruire joueur 1
    Missions* j = new Missions(1);
    objectifs.append(j);

    Missions* k = new Missions(2);
    objectifs.append(k);

    Missions* l = new Missions(3);
    objectifs.append(l);

    Missions* m = new Missions(4);
    objectifs.append(m);

    Missions* n = new Missions(5);
    objectifs.append(n);

    // 6 joueurs max de 0 à 5 prévoir à redistibuer sur le joueur x n'existe pas et si x est détruit par la mauvaise personne ou si on est soit même x alors on passe à mission b, 24 territoires.

}

// fonction appellée uniquement par le joueur en cours. donc pas de condition if joueur_en_cours
void PlateauRisQ::appelleRenforts()
{

    int total = 0;
    int nbpays = joueurs()[getJoueur_en_cours()]->getNbEtat();
    int id_tmp;
    total += nbpays/3;

    foreach (Continent *c, continentsList)
    {
        if (est_au_meme_joueur(c,joueurs()[getJoueur_en_cours()]))
        {
            id_tmp = c->id;
            switch(id_tmp)
            {
            case 1: // nord
                total += 5;
                break;
            case 2: // sud
                total += 2;
                break;
            case 3: // afrique
                total += 3;
                break;
            case 4: // europe
                total += 5;
                break;
            case 5: // asie
                total += 7;
                break;
            case 6: // océanie
                total += 2;
                break;
            default:
                break;
            }
        }
   }

    // prévoir un espace pour appeler une fonction gérant les cartes distribuées à chaque victoire pour faire des combinaisons permettant un bonus de renfort.
    joueurs()[getJoueur_en_cours()]->setNbPionRestant(total);
}


bool PlateauRisQ::est_au_meme_joueur(Continent* c, Joueur* j)
{
    if (c == NULL)
    {
        LOG(ODEBUG, "Continent NULL");
        exit(1);
    }
    if (j == NULL)
    {
        LOG(ODEBUG, "Joueur NULL");
        exit(2);
    }
    int taille = c->etats().count();

    if (taille == 0)
    {
         LOG(ODEBUG, "Continent vide, pas d'états.");
         qDebug() << "continent " << c ;
         qDebug() << "etatliste " << c->etats();
         return false;
    }

    int id_joueur = j->getId();
    for (int i = 0; i < taille ; i++)
    {
        if (c->etats()[i]->getJoueur()->getId() != id_joueur )
            return false;
    }

    return true ;

}

int PlateauRisQ::nbr_etats_acquis( Joueur* j)
{

    if (j == NULL)
    {
        LOG(ODEBUG, "Joueur NULL");
        exit(2);
    }


    int total = 0 ;


    int id_joueur = j->getId();


    foreach (Etat* e, etats() )
    {
        if (e->getJoueur()->getId()== id_joueur)
        {
            total++ ;
        }
    }
    return total;
}




bool PlateauRisQ::taille_min_armee( Joueur* j, int nb_etats_min, int taille_min_voulue)
{
    int nb_etats_valides = 0;
    int nb_etats = 0;
    if (j == NULL)
    {
        LOG(ODEBUG, "Joueur NULL");
        exit(2);
    }

    int resultat = -1 ;
    int tmp_nb_pions;

    int id_joueur = j->getId();


    foreach (Etat* e, etats() )
    {
        // si l'état appartient à j
        if (e->getJoueur()->getId()== id_joueur)
        {
            nb_etats++;


            tmp_nb_pions = 1 ; // pion central
            tmp_nb_pions += e->pions().count();

            if (tmp_nb_pions >= taille_min_voulue)
                nb_etats_valides++;
            if (resultat == -1)// initialisation
                resultat = tmp_nb_pions; // c'était pour connaitre la plus petite armée existante pour un joueur mais inutile désormais
            if (tmp_nb_pions < resultat)
                resultat = tmp_nb_pions;

        }
    }
    if (nb_etats_valides >=  nb_etats_min)
        return true;
    else
        return false;
}







int PlateauRisQ::taille_max_armee( Joueur* j)
{
    if (j == NULL)
    {
        LOG(ODEBUG, "Joueur NULL");
        exit(2);
    }
    int nb_etats = etats().count();
    int resultat = 0 ;
    int tmp_nb_pions ;

    if (nb_etats == 0)
    {
         LOG(ODEBUG, "Plateau vide, pas d'états.");
         return resultat;
    }

    int id_joueur = j->getId();


    for (int i = 0; i < nb_etats; i++)
    {
        // si l'état appartient à j
        if (etats()[i]->getJoueur()->getId()== id_joueur)
        {
            // je compte le nombre de pions sur cet état +1 (pion central)
            tmp_nb_pions = 1 ;
            tmp_nb_pions += etats()[i]->pions().count();
            if (tmp_nb_pions > resultat)
                resultat = tmp_nb_pions;

        }
    }
    return resultat;
}

void PlateauRisQ::placementInitial(Etat* etat)
{
    Pion *pion = new Pion();
    etat->addPion(pion);
    emit ChangeStatut();
}

void PlateauRisQ::placementRenfort(Etat* etat)
{
    Pion *pion = new Pion();
    etat->addPion(pion);
    if ( etat->getJoueur()->getNbPionRestant() == 0 )
        emit ChangeStatut();
}

void PlateauRisQ::attaque(int idAttaquant, int idCible)
{
    int nDa;
    int nDc;
    int n;

    int limNDa = 3;
    int limNDc = 2;

    Etat* etatAttaquant = etats()[idAttaquant];
    Etat* etatCible =  etats()[idCible];


    if(etatAttaquant->pions().count()>=limNDa) // si assez de pions
        nDa = limNDa; // on en prend 3 en attaque
    else nDa = etatAttaquant->pions().count(); // sinon on prend ce qu'il y a

    if(etatCible->pions().count()+1>=limNDc) // si assez de pions en défense
        nDc = limNDc; // on en prend 2
    else nDc = etatCible->pions().count()+1; // sinon la défense +1(central)

    if( nDa < nDc ) n = nDa; // n est le plus petit
    else n = nDc;

    QList <int> Da;
    QList <int> Dc;

    LOG(ONOTICE, "Jet(s) d'attaque");
    for(int i=0; i<nDa; i++){
        Da.append(1+qrand()%6);
        LOG2(ONOTICE, "Dès n°%1 : %2",i+1,Da.value(i));
    }

    LOG(ONOTICE, "Jet(s) de défense");
    for(int i=0; i<nDc; i++){
        Dc.append(1+qrand()%6);
        LOG2(ONOTICE, "Dès n°%1 : %2",i+1,Dc.value(i));
    }

    qSort(Da.begin(),Da.end());
    qSort(Dc.begin(),Dc.end());

    int nmA = 0;
    int nmC = 0; // nbr pions défenseur à éliminer

    // n est le plus petit (quantité de dés)
    for(int i=0; i<n; i++){
        LOG2(ONOTICE, "%1 : %2",Da.value(nDa-1-i),Dc.value(nDc-1-i));
        if(Da.value(nDa-1-i)>Dc.value(nDc-1-i)) // oui, le qSort tri par ordre croissant
            nmC++;
        else nmA++;
    }

    LOG1(ONOTICE, "l'attaquant perd %1 troupes",nmA);
    for(int i=0; i<nmA; i++){
        etatAttaquant->removeLastPion();
        //etatAttaquant->getJoueur()->setNbPionRestant(etatAttaquant->getJoueur()->getNbPionRestant()-1);
    }

    LOG1(ONOTICE, "la cible perd %1 troupes",nmC);

    if( nmC > etatCible->pions().count() )
    {
        LOG(ODEBUG, "cas 1");
        //l'attaquant conquiert le pays
        for(int i=0; i<nmC-1; i++){
            etatCible->removeLastPion();
            //etatCible->getJoueur()->setNbPionRestant(etatCible->getJoueur()->getNbPionRestant()-1);
        }

        QString data = QString::number(idCible);
        data.append(";");
        data.append(QString::number(etatAttaquant->getJoueur()->getId()));




        // changement prématuré de la part de l'attaquant pour l'état gagné parce que le signal (emit)
        // se propage trop lentement et interfère avec le addpion
        // changement aussi de vaincupar prématurément
        Joueur* pointeur_joueur_perdant = etatCible->getJoueur() ;
        LOG1(ODEBUG,"Nombre d'états pour joueur perdant son état est %1", nbr_etats_acquis(pointeur_joueur_perdant) );
        if (nbr_etats_acquis(pointeur_joueur_perdant) <= 1 )//alors il va le perdre
        {
            pointeur_joueur_perdant->setVaincupar(etatAttaquant->getJoueur()->getId()); // et dans cette fonction tout le monde le sait.
        }


        etatCible->setJoueur(etatAttaquant->getJoueur());
        emit ChangeJoueur(data);

        etatAttaquant->removeLastPion();
        //etatAttaquant->getJoueur()->setNbPionRestant(etatAttaquant->getJoueur()->getNbPionRestant()-1);
        etatAttaquant->getJoueur()->setNbPion(etatAttaquant->getJoueur()->getNbPion()); // pas de +1
        for(int i=0; i<(nDa-nmA-1); i++){
            etatAttaquant->removeLastPion();
            Pion *pion = new Pion();
            etatCible->addPion(pion);
        }
    }
    else
    {
        LOG(ODEBUG, "cas 2");
        for(int i=0; i<nmC; i++){
            etatCible->removeLastPion();
            //etatCible->getJoueur()->setNbPionRestant(etatCible->getJoueur()->getNbPionRestant()-1);
        }
    }

    //emit ChangeStatut();
}
